Utilizando como referencia la tabla sobre actividad física y gasto calórico disponible en la página W3Schools.com, desarrolla un modelo de Machine Learning eficiente para la predicción, aplicando técnicas de análisis y selección de variables relevantes.
# Importar librerias
import pandas as pd
import plotly.io as pio
# Renderirzar graficos de plotly para verlos en el navegador web
pio.renderers.default = 'notebook'
# Leer datos descargados de W3School - Actividzd fisica y gasto calorico
df = pd.read_csv("../data/data_calorias.csv")
print(df)
Duration Pulse Maxpulse Calories 0 60 110 130 409.1 1 60 117 145 479.0 2 60 103 135 340.0 3 45 109 175 282.4 4 45 117 148 406.0 .. ... ... ... ... 164 60 105 140 290.8 165 60 110 145 300.0 166 60 115 145 310.2 167 75 120 150 320.4 168 75 125 150 330.4 [169 rows x 4 columns]
print("**! Estructura de la tabla !**")
print(df.info())
print("**! Estadisticas descriptivas !**")
print(df.describe())
**! Estructura de la tabla !**
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 169 entries, 0 to 168
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Duration 169 non-null int64
1 Pulse 169 non-null int64
2 Maxpulse 169 non-null int64
3 Calories 164 non-null float64
dtypes: float64(1), int64(3)
memory usage: 5.4 KB
None
**! Estadisticas descriptivas !**
Duration Pulse Maxpulse Calories
count 169.000000 169.000000 169.000000 164.000000
mean 63.846154 107.461538 134.047337 375.790244
std 42.299949 14.510259 16.450434 266.379919
min 15.000000 80.000000 100.000000 50.300000
25% 45.000000 100.000000 124.000000 250.925000
50% 60.000000 105.000000 131.000000 318.600000
75% 60.000000 111.000000 141.000000 387.600000
max 300.000000 159.000000 184.000000 1860.400000
La variable objetivo (y, en este caso Calories) contiene 5 valores nulos (NaN). Scikit-learn no puede procesar datos faltantes directamente, por lo que es necesario limpiar o manejar estos valores antes de entrenar el modelo.
# Verificar valores nulos
print(f"**! Varaibles con valores nulos: Dataframe original !**\n{df.isna().sum()}")
# Manejar valores nulos en la variable objetivo
df = df.dropna(subset=["Calories"]) # O imputar: df["Calories"].fillna(df["Calories"].mean(), inplace=True)
# Asegúrate de que no haya valores nulos
print(f"**! Varaibles con valores nulos: Dataframe depurado !**\n{df.isna().sum()}")
**! Varaibles con valores nulos: Dataframe original !** Duration 0 Pulse 0 Maxpulse 0 Calories 5 dtype: int64 **! Varaibles con valores nulos: Dataframe depurado !** Duration 0 Pulse 0 Maxpulse 0 Calories 0 dtype: int64
Nota: En un modelo de clasificación, es esencial que la variable objetivo sea categórica, ya que el propósito del modelo es asignar cada instancia (registro) a una categoría predefinida o etiqueta. Dado que estamos utilizando datos de un ejemplo originalmente diseñado para regresión, convertiremos las calorías en categorías para adaptar el problema y facilitar su ejemplificación en el contexto de clasificación.
# Calcular percentiles para establecer límites dinámicos y balancerar las categorias
# Funcion para categorización de Calories
def categorizar_calorias(Calories):
if Calories < 200:
return 'Baja'
elif 200 <= Calories < 400:
return 'Media'
else:
return 'Alta'
# Aplicar la función al dataset
df['Calories_Category'] = df['Calories'].apply(categorizar_calorias)
print(df)
Duration Pulse Maxpulse Calories Calories_Category 0 60 110 130 409.1 Alta 1 60 117 145 479.0 Alta 2 60 103 135 340.0 Media 3 45 109 175 282.4 Media 4 45 117 148 406.0 Alta .. ... ... ... ... ... 164 60 105 140 290.8 Media 165 60 110 145 300.0 Media 166 60 115 145 310.2 Media 167 75 120 150 320.4 Media 168 75 125 150 330.4 Media [164 rows x 5 columns]
# Correlación entre variables numéricas independientes
corr_matrix = df[['Duration', 'Pulse', 'Maxpulse']].corr()
print(corr_matrix)
Duration Pulse Maxpulse Duration 1.000000 -0.160661 0.005679 Pulse -0.160661 1.000000 0.784631 Maxpulse 0.005679 0.784631 1.000000
import plotly.express as px
# Crear el heatmap de correlación
fig = px.imshow(corr_matrix, text_auto=True, color_continuous_scale='Blues', title="Heatmap de Correlación")
fig.show()
Pulse vs. Maxpulse: La correlación es 0.7846, lo que indica una relación fuerte entre Pulse y Maxpulse. Esto es un indicativo de que estas dos variables podrían estar altamente correlacionadas, lo que significa que podrían estar proporcionando información redundante al modelo. En este caso, eliminaremos una de estas variables (Pulse) del modelo para evitar problemas de colinealidad.
# Verificación de clases de la variable objetivo
df['Calories_Category'].value_counts()
Calories_Category Media 106 Alta 37 Baja 21 Name: count, dtype: int64
import plotly.express as px
# Crear el histograma con Plotly
fig = px.histogram(df, x='Calories_Category', title='Distribución de las Categorías de Calorías')
# Mostrar el gráfico
fig.show()
La distribución de las clases muestra un notable desbalance: la categoría "Media" tiene significativamente más instancias (106) en comparación con "Alta" (37) y "Baja" (21). Es necesario analizar más profundamente el reporte de clasificación y la matriz de confusión para interpretar correctamente el desempeño en las clases minoritarias ("Alta" y "Baja").
import plotly.express as px
# Suponiendo que 'df' es tu dataframe y tienes las columnas 'Duracion', 'Maxpulse' y 'Calories_Category'
fig = px.scatter_matrix(df,
dimensions=["Duration", "Pulse", "Maxpulse"],
color="Calories_Category",
title="Matriz de Dispersión: Duració y Máximo Pulso")
fig.show()
Existe una buena separacion entre las categorias (Baja, Media, Alta), esto sugiere que la clasificación basada en las variables (Duración y Pulso Maximo) podría funcionar bien. Si se solapan demasiado, podría indicar que las variables no son tan predictivas.
Correlación entre variables predictoras:
Pulse y Maxpulse: Como Pulso y Máximo Pulso están fuertemente correlacionados (0.7846), sería conveniente eliminar una de estas dos (Pulso).
Duración: Dado que Duración tiene una correlación muy baja tanto con Pulso como con Máximo Pulso, puede seguir siendo una variable útil para el modelo de predicción de calorías.
Utilizaremos un Modelo de Regresión Logística Multiclase, dado que tienes tres categorías para las calorías, la regresión logística multiclase es un modelo adecuado. Este modelo puede predecir la probabilidad de que una observación pertenezca a cada una de las tres clases, y luego asignar la clase con la mayor probabilidad.
Modelo de Regresion Logística
# importar librerias
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# Codificar las categorías con números (esto es necesario para los modelos de clasificación)
le = LabelEncoder()
df['Calories_Category'] = le.fit_transform(df['Calories_Category'])
# Seleccionar las variables predictoras y la variable objetivo (categoría)
X = df[["Duration", "Maxpulse"]] # Variables predictoras
y = df["Calories_Category"] # Variable objetivo transformada a categorías
# Dividir los datos en conjuntos de entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Crear y entrenar el modelo de clasificación (Regresión Logística)
model = LogisticRegression()
model.fit(X_train, y_train)
# Realizar predicciones
y_pred = model.predict(X_test)
# Evaluar el modelo
print("Exactitud del modelo:", accuracy_score(y_test, y_pred))
print("\nReporte de clasificación:\n", classification_report(y_test, y_pred))
print("\nMatriz de confusión:\n", confusion_matrix(y_test, y_pred))
# Ejemplo de predicción
new_data = pd.DataFrame({"Duration": [70], "Maxpulse": [140]})
predicted_category = model.predict(new_data)
predicted_category_label = le.inverse_transform(predicted_category)
print("\nCategoría predicha para las calorías:", predicted_category_label)
Exactitud del modelo: 0.8484848484848485
Reporte de clasificación:
precision recall f1-score support
0 1.00 0.67 0.80 6
1 0.67 0.80 0.73 5
2 0.87 0.91 0.89 22
accuracy 0.85 33
macro avg 0.85 0.79 0.81 33
weighted avg 0.86 0.85 0.85 33
Matriz de confusión:
[[ 4 0 2]
[ 0 4 1]
[ 0 2 20]]
Categoría predicha para las calorías: ['Media']
Interpretación del modelo
El modelo tiene un buen desempeño global, pero:
Exportar a HTML
jupyter nbconvert --to html notebook/regresion_logistica.ipynb